<?php

namespace app\common\service\course;

use app\common\enums\AppointmentIntervalEnum;
use app\common\enums\AppointmentStatusEnum;
use app\common\enums\CoreCourseTypeEnum;
use app\common\enums\CourseFullTypeEnum;
use app\common\enums\TcScheduleStatusEnum;
use app\common\enums\TrainerTimeStatusEnum;
use app\common\model\banxi\core\CoreAccountClassHour;
use app\common\model\banxi\core\CoreCourseAppointment;
use app\common\model\banxi\core\CoreTrainerTime;
use app\common\model\banxi\core\CoreTrialClassOrder;
use app\common\model\banxi\course\Course;
use app\common\model\banxi\grammar\GrammarCategory;
use app\common\model\banxi\organization\Organization;
use app\common\model\banxi\say\SayTeachSentence;
use app\common\model\banxi\student\Student;
use app\common\model\banxi\subject\SubjectTeach;
use app\common\model\banxi\trainer\Trainer;
use app\common\service\ApiService;
use app\common\service\student\AccountClassHourService;
use app\common\service\WeAppletService;
use ba\Exception;
use think\db\exception\PDOException;
use think\exception\ValidateException;
use think\facade\Cache;
use think\facade\Db;

class CourseAppointmentService extends ApiService
{

    public function getTemplateId()
    {
        return env('SUBSCRIBE_MESSAGE.TEMPLATE_ID');
    }

    public function sendKaiKeMsg($appointmentId)
    {
        $coreCourseAppointmentModel = new CoreCourseAppointment();
        $appointment = $coreCourseAppointmentModel->where('id', $appointmentId)->find();
        if (!$appointment) {
            return;
        }
        $studentId = $appointment['studentId'];
        if ($studentId == null) {
            return;
        }
        $studentModel = new Student();
        $student = $studentModel->where('id', $studentId)->field('id,name,subscribeNum,weMiniOpenid')->find();
        if (!$student) {
            return;
        }
        $courseFullType = $appointment['courseFullType'];
        $courseName = $appointment['courseName'];
        $startTime = $appointment['startTime'];
        $trainerId = $appointment['trainerId'];
        if ($student['subscribeNum'] > 0 && !empty($student['weMiniOpenid'])) {
            $studentModel->subSubscribeNum($studentId);
            $trainerName = Trainer::where('id', $trainerId)->value('name');
            $weAppletService = new WeAppletService();
            $templateId = $this->getTemplateId();
            $thing31 = (new CourseFullTypeEnum)->getDescription($courseFullType);
            $courseName = mb_substr($courseName, 0, 20);
            $templateData = [
                //分类
                'thing31' => ['value' => $thing31],
                //课程名称
                "thing26" => ['value' => $courseName],
                //上课时间
                'time25' => ['value' => $startTime],
                //授课老师
                'thing19' => ['value' => $trainerName],
            ];
            logSave([
                'appointmentId' => $appointmentId,
                'templateData' => $templateData,
                'templateId' => $templateId,
                'student' => [
                    'name' => $student['name'],
                    'openid' => $student['weMiniOpenid'],
                ],
                'msg'=>'开课订阅消息发送数据'
            ],'sendKaiKeMsg');
            $weAppletService->sendSubscribeMessage($student['weMiniOpenid'], $templateId, $templateData);
        }
        $coreCourseAppointmentModel->where('id', $appointmentId)->update(['isKaiKeMsgSend' => 1]);
    }

    public function trainerAppointment(array $appointment, string $organizationId = '0')
    {
        Db::startTrans();
        try {
            $itemList = $appointment['itemList'];
            $courseFullType = $appointment['courseFullType'];
            $studentId = $appointment['studentId'];
            $courseType = $appointment['courseType'];
            $courseId = $appointment['courseId'];
            $trainerIds = array_unique(array_map(function ($item) {
                return $item['trainerId'];
            }, $itemList));
            $courseFullTypeEnum = new CourseFullTypeEnum();
            $minMinutes = $courseFullTypeEnum->getMinMinutes($courseFullType);
            $dateMap = [];
            foreach ($itemList as $item) {
                $dateMap[$item['date']][] = $item;
            }
            $dateNow = date('Y-m-d'); //当前日期
            $dateTimeNow = strtotime(date('Y-m-d H:i:s')); // 当前时间戳
            foreach ($dateMap as $date => $items) {
                $dateFmt = $date;
                if (strtotime($date) < strtotime($dateNow)) {
                    throw new ValidateException(sprintf('日期不能小于今天 %s', $dateFmt));
                }
                usort($items, function ($a, $b) {
                    $timeA = strtotime($a['startTime']);
                    $timeB = strtotime($b['startTime']);
                    return $timeA <=> $timeB;
                });
                for ($i = 0; $i < count($items); $i++) {
                    $currentItem = $items[$i];
                    $startTime = $currentItem['startTime'];
                    $dateTime = strtotime("{$date} {$startTime}");

                    // 时间不能低于当前时间
                    if ($dateTime < $dateTimeNow) {
                        throw new ValidateException("时间不能低于现在 {$dateFmt} {$startTime}");
                    }

                    // 验证与上一项的时间间隔
                    if ($i > 0) {
                        $topItem = $items[$i - 1];
                        $topStartTime = $topItem['startTime'];
                        $localTime = strtotime("{$date} {$topStartTime}") + $minMinutes * 60;

                        if ($dateTime < $localTime) {
                            throw new ValidateException("您已经选择了 {$dateFmt} {$topStartTime}");
                        }
                        if ($dateTime === $localTime) {
                            throw new ValidateException("时间重复 {$dateFmt} {$topStartTime}");
                        }
                    }
                }
            }

            //获取学员
            $student = Student::where(['id' => $studentId])->find();
            if (!$student) {
                throw new ValidateException('学员不存在');
            }
            if ($organizationId) {
                $organization = new Organization();
                $sonOrganization = $organization->getSonOrganization($organizationId);
                if (!in_array($student['organizationId'], $sonOrganization)) {
                    throw new ValidateException('操作失败，权限不足');
                }
            }
            $coreTrainerTime = new CoreTrainerTime();
            // 获取课程&语法课程
            if ($courseType == CoreCourseTypeEnum::ORDINARY) {
                $course = Course::where(['id' => $courseId])->find();
                if (!$course) {
                    throw new ValidateException('课程不存在');
                }
                if ($courseFullType == CourseFullTypeEnum::EXPERIENCE && !$course['courseExperience']) {
                    throw new ValidateException('您选择的课程不是体验课');
                }
                $trainerList = Trainer::whereIn('id', $trainerIds)->select();
                if (empty($trainerList)) {
                    throw new ValidateException('教练不存在');
                }
                if (count($trainerList) != count($trainerIds)) {
                    throw new ValidateException('请重新刷新页面排课，当前排课中教练数据异常');
                }
                $locks = $coreTrainerTime->lockTrainers($trainerList);
                try {
                    $dateList = array_keys($dateMap);
                    $trainerTimeList = $coreTrainerTime->listNotIdleByTrainerAndDateIn($trainerIds, $dateList);
                    $minutes = $courseFullTypeEnum->getMinutes($courseFullType);
                    $studentOrganizationId = $student['organizationId'];
                    $courseName = $course['name'];
                    $courseAppointmentList = [];
                    foreach ($itemList as $item) {
                        $trainerId = $item['trainerId'];
                        $timeFmt = $item['timeFmt'];
                        $date = $item['date'];
                        $startTime = $item['startTime'];
                        $endTime = date('H:i', strtotime("$timeFmt") + $minutes * 60);
                        $courseAppointment = $appointment;
                        $courseAppointment['studentOrganizationId'] = $studentOrganizationId;
                        $courseAppointment['courseName'] = $courseName;
                        $courseAppointment['trainerId'] = $trainerId;
                        $courseAppointment['date'] = $date;
                        $courseAppointment['startTime'] = $startTime;
                        $courseAppointment['endTime'] = $endTime;
                        $courseAppointmentList[] = $courseAppointment;
                        $trainerTimeList[] = [
                            'trainerId' => $trainerId,
                            'date' => $date,
                            'startTime' => $startTime,
                            'endTime' => $endTime,
                            'status' => TrainerTimeStatusEnum::APPOINTMENT
                        ];
                    }
                    $res = $this->appointmentUpdateDB($student, $courseFullType, $dateList, $trainerIds, $trainerTimeList, $courseAppointmentList);
                    if (!$res) {
                        throw new ValidateException($this->getError());
                    }
                } catch (Exception|PDOException|ValidateException $e) {
                    throw new ValidateException($e->getMessage());
                } finally {
                    foreach ($locks as $item) {
                        Cache::delete($item);
                    }
                }
            } else if ($courseType == CoreCourseTypeEnum::GRAMMAR) {
                $grammarCategory = GrammarCategory::where(['id' => $courseId])->find();
                if (!$grammarCategory) {
                    throw new ValidateException('语法课程不存在');
                }
                if ($courseFullType == CourseFullTypeEnum::EXPERIENCE) {
                    throw new ValidateException('语法课程无法选择体验课');
                }
                $trainerList = Trainer::whereIn('id', $trainerIds)->select();
                if (empty($trainerList)) {
                    throw new ValidateException('教练不存在');
                }
                if (count($trainerList) != count($trainerIds)) {
                    throw new ValidateException('请重新刷新页面排课，当前排课中教练数据异常');
                }
                $locks = $coreTrainerTime->lockTrainers($trainerList);
                try {
                    $dateList = array_keys($dateMap);
                    $trainerTimeList = $coreTrainerTime->listNotIdleByTrainerAndDateIn($trainerIds, $dateList);
                    $minutes = $courseFullTypeEnum->getMinutes($courseFullType);
                    $studentOrganizationId = $student['organizationId'];
                    $courseName = $grammarCategory['name'];
                    $courseAppointmentList = [];
                    foreach ($itemList as $item) {
                        $trainerId = $item['trainerId'];
                        $timeFmt = $item['timeFmt'];
                        $date = $item['date'];
                        $startTime = $item['startTime'];
                        $endTime = date('H:i', strtotime("$timeFmt") + $minutes * 60);
                        $courseAppointment = $appointment;
                        $courseAppointment['studentOrganizationId'] = $studentOrganizationId;
                        $courseAppointment['courseType'] = CoreCourseTypeEnum::GRAMMAR;
                        $courseAppointment['courseName'] = $courseName;
                        $courseAppointment['trainerId'] = $trainerId;
                        $courseAppointment['date'] = $date;
                        $courseAppointment['startTime'] = $startTime;
                        $courseAppointment['endTime'] = $endTime;
                        $courseAppointmentList[] = $courseAppointment;
                        $trainerTimeList[] = [
                            'trainerId' => $trainerId,
                            'date' => $date,
                            'startTime' => $startTime,
                            'endTime' => $endTime,
                            'status' => TrainerTimeStatusEnum::APPOINTMENT
                        ];
                    }
                    $res = $this->appointmentUpdateDB($student, $courseFullType, $dateList, $trainerIds, $trainerTimeList, $courseAppointmentList);
                    if (!$res) {
                        throw new ValidateException($this->getError());
                    }
                } catch (Exception|PDOException|ValidateException $e) {
                    throw new ValidateException($e->getMessage());
                } finally {
                    foreach ($locks as $item) {
                        Cache::delete($item);
                    }
                }
            } else if ($courseType == CoreCourseTypeEnum::SUBJECT) {
                $subjectTeach = SubjectTeach::where(['id' => $courseId])->find();
                if (!$subjectTeach) {
                    throw new ValidateException('视频课程不存在');
                }
                if ($courseFullType == CourseFullTypeEnum::EXPERIENCE) {
                    throw new ValidateException('视频课程无法选择体验课');
                }
                $trainerList = Trainer::whereIn('id', $trainerIds)->select();
                if (empty($trainerList)) {
                    throw new ValidateException('教练不存在');
                }
                if (count($trainerList) != count($trainerIds)) {
                    throw new ValidateException('请重新刷新页面排课，当前排课中教练数据异常');
                }
                $locks = $coreTrainerTime->lockTrainers($trainerList);
                try {
                    $dateList = array_keys($dateMap);
                    $trainerTimeList = $coreTrainerTime->listNotIdleByTrainerAndDateIn($trainerIds, $dateList);
                    $minutes = $courseFullTypeEnum->getMinutes($courseFullType);
                    $studentOrganizationId = $student['organizationId'];
                    $courseName = $subjectTeach['name'];
                    $courseAppointmentList = [];
                    foreach ($itemList as $item) {
                        $trainerId = $item['trainerId'];
                        $timeFmt = $item['timeFmt'];
                        $date = $item['date'];
                        $startTime = $item['startTime'];
                        $endTime = date('H:i', strtotime("$timeFmt") + $minutes * 60);
                        $courseAppointment = $appointment;
                        $courseAppointment['studentOrganizationId'] = $studentOrganizationId;
                        $courseAppointment['courseType'] = CoreCourseTypeEnum::SUBJECT;
                        $courseAppointment['courseName'] = $courseName;
                        $courseAppointment['trainerId'] = $trainerId;
                        $courseAppointment['date'] = $date;
                        $courseAppointment['startTime'] = $startTime;
                        $courseAppointment['endTime'] = $endTime;
                        $courseAppointmentList[] = $courseAppointment;
                        $trainerTimeList[] = [
                            'trainerId' => $trainerId,
                            'date' => $date,
                            'startTime' => $startTime,
                            'endTime' => $endTime,
                            'status' => TrainerTimeStatusEnum::APPOINTMENT
                        ];
                    }
                    $res = $this->appointmentUpdateDB($student, $courseFullType, $dateList, $trainerIds, $trainerTimeList, $courseAppointmentList);
                    if (!$res) {
                        throw new ValidateException($this->getError());
                    }
                } catch (Exception|PDOException|ValidateException $e) {
                    throw new ValidateException($e->getMessage());
                } finally {
                    foreach ($locks as $item) {
                        Cache::delete($item);
                    }
                }
            } else if ($courseType == CoreCourseTypeEnum::SENTENCE) {
                $sayTeachSentence = SayTeachSentence::where(['id' => $courseId])->find();
                if (!$sayTeachSentence) {
                    throw new ValidateException('课程阶段不存在');
                }
                if ($courseFullType == CourseFullTypeEnum::EXPERIENCE) {
                    throw new ValidateException('句型课程无法选择体验课');
                }
                $trainerList = Trainer::whereIn('id', $trainerIds)->select();
                if (empty($trainerList)) {
                    throw new ValidateException('教练不存在');
                }
                if (count($trainerList) != count($trainerIds)) {
                    throw new ValidateException('请重新刷新页面排课，当前排课中教练数据异常');
                }
                $locks = $coreTrainerTime->lockTrainers($trainerList);
                try {
                    $dateList = array_keys($dateMap);
                    $trainerTimeList = $coreTrainerTime->listNotIdleByTrainerAndDateIn($trainerIds, $dateList);
                    $minutes = $courseFullTypeEnum->getMinutes($courseFullType);
                    $studentOrganizationId = $student['organizationId'];
                    $courseName = $sayTeachSentence['name'];
                    $courseAppointmentList = [];
                    foreach ($itemList as $item) {
                        $trainerId = $item['trainerId'];
                        $timeFmt = $item['timeFmt'];
                        $date = $item['date'];
                        $startTime = $item['startTime'];
                        $endTime = date('H:i', strtotime("$timeFmt") + $minutes * 60);
                        $courseAppointment = $appointment;
                        $courseAppointment['studentOrganizationId'] = $studentOrganizationId;
                        $courseAppointment['courseType'] = CoreCourseTypeEnum::SENTENCE;
                        $courseAppointment['courseName'] = $courseName;
                        $courseAppointment['trainerId'] = $trainerId;
                        $courseAppointment['date'] = $date;
                        $courseAppointment['startTime'] = $startTime;
                        $courseAppointment['endTime'] = $endTime;
                        $courseAppointmentList[] = $courseAppointment;
                        $trainerTimeList[] = [
                            'trainerId' => $trainerId,
                            'date' => $date,
                            'startTime' => $startTime,
                            'endTime' => $endTime,
                            'status' => TrainerTimeStatusEnum::APPOINTMENT
                        ];
                    }
                    $res = $this->appointmentUpdateDB($student, $courseFullType, $dateList, $trainerIds, $trainerTimeList, $courseAppointmentList);
                    if (!$res) {
                        throw new ValidateException($this->getError());
                    }
                } catch (Exception|PDOException|ValidateException $e) {
                    throw new ValidateException($e->getMessage());
                } finally {
                    foreach ($locks as $item) {
                        Cache::delete($item);
                    }
                }
            } else {
                throw new ValidateException('暂不支持该课程类型');
            }
            Db::commit();
        } catch (Exception|PDOException|ValidateException $e) {
            Db::rollback();
            $this->setError($e->getMessage());
            return false;
        }
        return true;
    }


    public function timeAppointment(array $appointment, string $organizationId = '0')
    {
        Db::startTrans();
        try {
            $courseFullType = $appointment['courseFullType'];
            $studentId = $appointment['studentId'];
            $courseId = $appointment['courseId'];
            $courseType = $appointment['courseType'];
            $trainerId = $appointment['trainerId'];
            $appointmentNum = $appointment['appointmentNum'];
            $appointmentInterval = $appointment['appointmentInterval'];
            $appointmentDate = $appointment['date'];
            $appointmentStartTime = $appointment['startTime'];
            $classLink = $appointment['classLink'] ?? '';
            $appointmentTime = strtotime($appointmentDate . ' ' . $appointmentStartTime);
            if ($appointmentTime < time()) {
                throw new ValidateException('预约时间不能低于现在');
            }
            $student = Student::where(['id' => $studentId])->find();
            if (!$student) {
                throw new ValidateException('学员不存在');
            }
            if ($organizationId) {
                $organization = new Organization();
                $sonOrganization = $organization->getSonOrganization($organizationId);
                if (in_array($student['organizationId'], $sonOrganization)) {
                    throw new ValidateException('操作失败，权限不足');
                }
            }

            $coreAccountClassHour = new CoreAccountClassHour();
            $accountClassHour = $coreAccountClassHour->getByStudentId($studentId);
            switch ($courseFullType) {
                case CourseFullTypeEnum::EXPERIENCE:
                    if ($courseType == CoreCourseTypeEnum::GRAMMAR) {
                        throw new ValidateException('语法课程无法预约体验课');
                    }
                    break;
                case CourseFullTypeEnum::C_30:
                    $class30Minute = $accountClassHour['class30Minute'];
                    if ($appointmentNum > $class30Minute) {
                        throw new ValidateException("课时不足，学员30分钟课时剩余：$class30Minute");
                    }
                    break;
                case CourseFullTypeEnum::C_60:
                    $class60Minute = $accountClassHour['class60Minute'];
                    if ($appointmentNum > $class60Minute) {
                        throw new ValidateException("课时不足，学员正课课时剩余：$class60Minute");
                    }
                    break;
            }
            $coreTrainerTime = new CoreTrainerTime();
            $courseFullTypeEnum = new CourseFullTypeEnum();
            if ($courseType == CoreCourseTypeEnum::ORDINARY) {
                $course = Course::where(['id' => $courseId])->find();
                if (!$course) {
                    throw new ValidateException('课程不存在');
                }
                if ($courseFullType == CourseFullTypeEnum::EXPERIENCE && !$course['courseExperience']) {
                    throw new ValidateException('您选择的课程不是体验课');
                }
                $trainer = Trainer::where(['id' => $trainerId])->find();
                if (!$trainer) {
                    throw new ValidateException('教练不存在');
                }
                $lock = $coreTrainerTime->lockTrainer($trainer);
                try {
                    $minutes = $courseFullTypeEnum->getMinutes($courseFullType);
                    $studentOrganizationId = $student['organizationId'];
                    $courseName = $course['name'];
                    $minutesInterval = (new AppointmentIntervalEnum)->getMinutes($appointmentInterval);
                    $courseAppointmentList = [];
                    for ($i = 0; $i < $appointmentNum; $i++) {
                        $localDate = date('Y-m-d', $appointmentTime);
                        $localStartTime = date('H:i:s', $appointmentTime);
                        $localEndTime = date('H:i:s', $appointmentTime + $minutes * 60);
                        $courseAppointment = $appointment;
                        $courseAppointment['studentOrganizationId'] = $studentOrganizationId;
                        $courseAppointment['courseName'] = $courseName;
                        $courseAppointment['date'] = $localDate;
                        $courseAppointment['startTime'] = $localStartTime;
                        $courseAppointment['endTime'] = $localEndTime;
                        $courseAppointmentList[] = $courseAppointment;
                        if ($minutesInterval >= 24 * 60) {
                            $appointmentTime = $appointmentTime + $minutesInterval * 60;
                        } else {
                            $appointmentTime = $appointmentTime + ($minutesInterval + $minutes) * 60;
                        }
                    }
                    $dateList = array_unique(array_column($courseAppointmentList, 'date'));
                    $trainerTimeList = $coreTrainerTime->listNotIdleByTrainerAndDateIn($trainerId, $dateList);
                    foreach ($courseAppointmentList as $item) {
                        $localDate = $item['date'];
                        $localStartTime = $item['startTime'];
                        $localEndTime = $item['endTime'];
                        $trainerTimeList[] = [
                            'trainerId' => $trainerId,
                            'date' => $localDate,
                            'startTime' => $localStartTime,
                            'endTime' => $localEndTime,
                            'status' => TrainerTimeStatusEnum::APPOINTMENT
                        ];
                    }
                    $res = $this->appointmentUpdateDB($student, $courseFullType, $dateList, [$trainerId], $trainerTimeList, $courseAppointmentList);
                    if (!$res) {
                        throw new ValidateException($this->getError());
                    }
                } catch (Exception|PDOException|ValidateException $e) {
                    throw new ValidateException($e->getError());
                } finally {
                    Cache::delete($lock);
                }
            } else if ($courseType == CoreCourseTypeEnum::GRAMMAR) {
                $grammarCategory = GrammarCategory::where(['id' => $courseId])->find();
                if (!$grammarCategory) {
                    throw new ValidateException('语法课程不存在');
                }
                if ($courseFullType == CourseFullTypeEnum::EXPERIENCE) {
                    throw new ValidateException('语法课程无法预约体验课');
                }
                $trainer = Trainer::where(['id' => $trainerId])->find();
                if (!$trainer) {
                    throw new ValidateException('教练不存在');
                }
                $lock = $coreTrainerTime->lockTrainer($trainer);
                try {
                    $minutes = $courseFullTypeEnum->getMinutes($courseFullType);
                    $studentOrganizationId = $student['organizationId'];
                    $courseName = $grammarCategory['name'];
                    $minutesInterval = (new AppointmentIntervalEnum)->getMinutes($appointmentInterval);
                    $courseAppointmentList = [];
                    for ($i = 0; $i < $appointmentNum; $i++) {
                        $localDate = date('Y-m-d', $appointmentTime);
                        $localStartTime = date('H:i:s', $appointmentTime);
                        $localEndTime = date('H:i:s', $appointmentTime + $minutes * 60);
                        $courseAppointment = $appointment;
                        $courseAppointment['studentOrganizationId'] = $studentOrganizationId;
                        $courseAppointment['courseName'] = $courseName;
                        $courseAppointment['date'] = $localDate;
                        $courseAppointment['startTime'] = $localStartTime;
                        $courseAppointment['endTime'] = $localEndTime;
                        $courseAppointmentList[] = $courseAppointment;
                        if ($minutesInterval >= 24 * 60) {
                            $appointmentTime = $appointmentTime + $minutesInterval * 60;
                        } else {
                            $appointmentTime = $appointmentTime + ($minutesInterval + $minutes) * 60;
                        }
                    }
                    $dateList = array_unique(array_column($courseAppointmentList, 'date'));
                    $trainerTimeList = $coreTrainerTime->listNotIdleByTrainerAndDateIn($trainerId, $dateList);
                    foreach ($courseAppointmentList as $item) {
                        $localDate = $item['date'];
                        $localStartTime = $item['startTime'];
                        $localEndTime = $item['endTime'];
                        $trainerTimeList[] = [
                            'trainerId' => $trainerId,
                            'date' => $localDate,
                            'startTime' => $localStartTime,
                            'endTime' => $localEndTime,
                            'status' => TrainerTimeStatusEnum::APPOINTMENT
                        ];
                    }
                    $res = $this->appointmentUpdateDB($student, $courseFullType, $dateList, [$trainerId], $trainerTimeList, $courseAppointmentList);
                    if (!$res) {
                        throw new ValidateException($this->getError());
                    }
                } catch (Exception|PDOException|ValidateException $e) {
                    throw new ValidateException($e->getError());
                } finally {
                    Cache::delete($lock);
                }
            } else if ($courseType == CoreCourseTypeEnum::SUBJECT) {
                $subjectTeach = SubjectTeach::where(['id' => $courseId])->find();
                if (!$subjectTeach) {
                    throw new ValidateException('视频课程不存在');
                }
                if ($courseFullType == CourseFullTypeEnum::EXPERIENCE) {
                    throw new ValidateException('视频课程无法预约体验课');
                }
                $trainer = Trainer::where(['id' => $trainerId])->find();
                if (!$trainer) {
                    throw new ValidateException('教练不存在');
                }
                $lock = $coreTrainerTime->lockTrainer($trainer);
                try {
                    $minutes = $courseFullTypeEnum->getMinutes($courseFullType);
                    $studentOrganizationId = $student['organizationId'];
                    $courseName = $subjectTeach['name'];
                    $minutesInterval = (new AppointmentIntervalEnum)->getMinutes($appointmentInterval);
                    $courseAppointmentList = [];
                    for ($i = 0; $i < $appointmentNum; $i++) {
                        $localDate = date('Y-m-d', $appointmentTime);
                        $localStartTime = date('H:i:s', $appointmentTime);
                        $localEndTime = date('H:i:s', $appointmentTime + $minutes * 60);
                        $courseAppointment = $appointment;
                        $courseAppointment['studentOrganizationId'] = $studentOrganizationId;
                        $courseAppointment['courseName'] = $courseName;
                        $courseAppointment['date'] = $localDate;
                        $courseAppointment['startTime'] = $localStartTime;
                        $courseAppointment['endTime'] = $localEndTime;
                        $courseAppointment['classLink'] = $classLink;
                        $courseAppointmentList[] = $courseAppointment;
                        if ($minutesInterval >= 24 * 60) {
                            $appointmentTime = $appointmentTime + $minutesInterval * 60;
                        } else {
                            $appointmentTime = $appointmentTime + ($minutesInterval + $minutes) * 60;
                        }
                    }
                    $dateList = array_unique(array_column($courseAppointmentList, 'date'));
                    $trainerTimeList = $coreTrainerTime->listNotIdleByTrainerAndDateIn($trainerId, $dateList);
                    foreach ($courseAppointmentList as $item) {
                        $localDate = $item['date'];
                        $localStartTime = $item['startTime'];
                        $localEndTime = $item['endTime'];
                        $trainerTimeList[] = [
                            'trainerId' => $trainerId,
                            'date' => $localDate,
                            'startTime' => $localStartTime,
                            'endTime' => $localEndTime,
                            'status' => TrainerTimeStatusEnum::APPOINTMENT
                        ];
                    }
                    $res = $this->appointmentUpdateDB($student, $courseFullType, $dateList, [$trainerId], $trainerTimeList, $courseAppointmentList);
                    if (!$res) {
                        throw new ValidateException($this->getError());
                    }
                } catch (Exception|PDOException|ValidateException $e) {
                    throw new ValidateException($e->getError());
                } finally {
                    Cache::delete($lock);
                }
            } else if ($courseType == CoreCourseTypeEnum::SENTENCE) {
                $sayTeachSentence = SayTeachSentence::where(['id' => $courseId])->find();
                if (!$sayTeachSentence) {
                    throw new ValidateException('句型不存在');
                }
                // if ($courseFullType == CourseFullTypeEnum::EXPERIENCE) {
                //     throw new ValidateException('句型课程无法预约体验课');
                // }
                $trainer = Trainer::where(['id' => $trainerId])->find();
                if (!$trainer) {
                    throw new ValidateException('教练不存在');
                }
                $lock = $coreTrainerTime->lockTrainer($trainer);
                try {
                    $minutes = $courseFullTypeEnum->getMinutes($courseFullType);
                    $studentOrganizationId = $student['organizationId'];
                    $courseName = $sayTeachSentence['name'];
                    $minutesInterval = (new AppointmentIntervalEnum)->getMinutes($appointmentInterval);
                    $courseAppointmentList = [];
                    for ($i = 0; $i < $appointmentNum; $i++) {
                        $localDate = date('Y-m-d', $appointmentTime);
                        $localStartTime = date('H:i:s', $appointmentTime);
                        $localEndTime = date('H:i:s', $appointmentTime + $minutes * 60);
                        $courseAppointment = $appointment;
                        $courseAppointment['studentOrganizationId'] = $studentOrganizationId;
                        $courseAppointment['courseName'] = $courseName;
                        $courseAppointment['date'] = $localDate;
                        $courseAppointment['startTime'] = $localStartTime;
                        $courseAppointment['endTime'] = $localEndTime;
                        $courseAppointment['classLink'] = $classLink;
                        $courseAppointmentList[] = $courseAppointment;
                        if ($minutesInterval >= 24 * 60) {
                            $appointmentTime = $appointmentTime + $minutesInterval * 60;
                        } else {
                            $appointmentTime = $appointmentTime + ($minutesInterval + $minutes) * 60;
                        }
                    }
                    $dateList = array_unique(array_column($courseAppointmentList, 'date'));
                    $trainerTimeList = $coreTrainerTime->listNotIdleByTrainerAndDateIn($trainerId, $dateList);
                    foreach ($courseAppointmentList as $item) {
                        $localDate = $item['date'];
                        $localStartTime = $item['startTime'];
                        $localEndTime = $item['endTime'];
                        $trainerTimeList[] = [
                            'trainerId' => $trainerId,
                            'date' => $localDate,
                            'startTime' => $localStartTime,
                            'endTime' => $localEndTime,
                            'status' => TrainerTimeStatusEnum::APPOINTMENT
                        ];
                    }
                    $res = $this->appointmentUpdateDB($student, $courseFullType, $dateList, [$trainerId], $trainerTimeList, $courseAppointmentList);
                    if (!$res) {
                        throw new ValidateException($this->getError());
                    }
                } catch (Exception|PDOException|ValidateException $e) {
                    throw new ValidateException($e->getError());
                } finally {
                    Cache::delete($lock);
                }
            } else {
                throw new ValidateException('暂不支持该课程类型');
            }
            Db::commit();
        } catch (Exception|PDOException|ValidateException $e) {
            Db::rollback();
            $this->setError($e->getMessage());
            return false;
        }
        return true;
    }

    public function updateAppointment(array $update, string $organizationId = null)
    {
        Db::startTrans();
        try {
            $id = $update['id'];
            $courseAppointment = CoreCourseAppointment::where(['id' => $id])->find();
            if (!$courseAppointment) {
                throw new ValidateException('预约不存在');
            }
            $studentOrganizationId = $courseAppointment['studentOrganizationId'];
            if ($organizationId) {
                $organization = new Organization();
                $sonOrganization = $organization->getSonOrganization($organizationId);
                if (!in_array($studentOrganizationId, $sonOrganization)) {
                    throw new ValidateException('操作失败，权限不足');
                }
            }
            $appointmentStatus = $courseAppointment['appointmentStatus'];
            if (!in_array($appointmentStatus, [AppointmentStatusEnum::PENDING_CLASS, AppointmentStatusEnum::EXPIRED])) {
                throw new ValidateException('修改预约失败，预约状态不为待上课或已过期');
            }
            $studentId = $courseAppointment['studentId'];
            $student = Student::where(['id' => $studentId])->find();
            $trainerId = $courseAppointment['trainerId'];
            $courseId = $courseAppointment['courseId'];
            $courseType = $courseAppointment['courseType'];
            $date = $courseAppointment['date'];
            $startTime = $courseAppointment['startTime'];
            $endTime = $courseAppointment['endTime'];
            $updateCourseId = $update['courseId'];
            $updateCourseType = $update['courseType'];
            $updateTrainerId = $update['trainerId'];
            $updateDate = $update['date'];
            $updateStartTime = $update['startTime'];
            $trainerIds = [];
            $execMethod = null;
            if ($updateTrainerId != $trainerId) {
                $execMethod = 0;
                $trainerIds[] = $trainerId;
                $trainerIds[] = $updateTrainerId;
            } else if ($updateDate != $date || $updateStartTime != $startTime) {
                $execMethod = 1;
                $trainerIds[] = $trainerId;
            }
            $appointmentTime = strtotime($updateDate . ' ' . $updateStartTime);
            if ($appointmentTime < time()) {
                throw new ValidateException('预约时间不能低于现在');
            }
            $trainerList = [];
            if (!empty($trainerIds)) {
                $trainerList = Trainer::where(['id' => $trainerIds])->select()->toArray();
            }
            $trainerIdList = array_column($trainerList, 'id');
            if ($execMethod === 0 && !in_array($updateTrainerId, $trainerIdList)) {
                throw new ValidateException('修改教练失败，该教练不存在');
            }
            $coreTrainerTime = new CoreTrainerTime();
            $courseFullTypeEnum = new CourseFullTypeEnum();
            $locks = $coreTrainerTime->lockTrainers($trainerList);
            try {
                $courseFullType = $courseAppointment['courseFullType'];
                $updateCourseFullType = $update['courseFullType'];
                $trainerTimeNewList = [];
                if ($execMethod != null) {
                    $coreTrainerTime->removeByIdleAndTrainerAndDateAndTime($trainerId, $date, $startTime, $endTime);
                    $trainerTimeList = $coreTrainerTime->listNotIdleByTrainerAndDateIn($updateTrainerId, [$updateDate]);
                    $timestamp = strtotime($updateDate . ' ' . $updateStartTime);

                    $minutes = $courseFullTypeEnum->getMinutes($updateCourseFullType);
                    $endTime = date('H:i:s', $timestamp + $minutes * 60);
                    $trainerTimeList[] = [
                        'trainerId' => $updateTrainerId,
                        'date' => $updateDate,
                        'startTime' => $updateStartTime,
                        'endTime' => $endTime,
                        'status' => TrainerTimeStatusEnum::APPOINTMENT
                    ];
                    $trainerTimeNewList = $coreTrainerTime->getPlanTimeIdle($trainerTimeList);
                }
                if (!empty($trainerTimeNewList)) {
                    $insertData = array_filter($trainerTimeNewList, function ($item) {
                        return !isset($item['id']);
                    });
                    // 过滤出需要更新的数据（id 不为空）
                    $updateData = array_filter($trainerTimeNewList, function ($item) {
                        return isset($item['id']);
                    });
                    if (!empty($insertData)) {
                        $coreTrainerTime->saveAll($insertData);
                    }
                    if (!empty($updateData)) {
                        foreach ($updateData as $item) {
                            $coreTrainerTime->update($item, ['id' => $item['id']]);
                        }
                    }
                }
                // 验证是否修改了课程类型
                if ($updateCourseFullType != $courseFullType) {
                    $classHourType = $courseFullTypeEnum->getClassHourType($courseFullType);
                    $accountClassHourService = new AccountClassHourService();
                    if ($classHourType) {
                        $cnt = $accountClassHourService->studentReleaseFrozen($student, $classHourType, 1);
                        if (!$cnt) {
                            throw new ValidateException($accountClassHourService->getError());
                        }
                    }
                    $updateClassHourType = $courseFullTypeEnum->getClassHourType($updateCourseFullType);
                    if ($updateClassHourType != null) {
                        $cnt = $accountClassHourService->studentFrozen($student, $updateClassHourType, 1);
                        if (!$cnt) {
                            throw new ValidateException($accountClassHourService->getError());
                        }
                    }
                }
                //数据修改
                $updateCourseAppointment = $update;
                $timestamp = strtotime($updateDate . ' ' . $updateStartTime);
                $minutes = $courseFullTypeEnum->getMinutes($updateCourseFullType);
                $endTime = date('H:i:s', $timestamp + $minutes * 60);
                $updateCourseAppointment['endTime'] = $endTime;
                $updateCourseAppointment['appointmentStatus'] = AppointmentStatusEnum::PENDING_CLASS;

                if ($updateCourseId != $courseId || $courseType != $updateCourseType) {
                    if ($updateCourseType == CoreCourseTypeEnum::ORDINARY) {
                        $courseName = Course::where(['id' => $updateCourseId])->value('name');
                    } else if ($updateCourseType == CoreCourseTypeEnum::GRAMMAR) {
                        $courseName = GrammarCategory::where(['id' => $updateCourseId])->value('name');
                    } else {
                        $courseName = SubjectTeach::where(['id' => $updateCourseId])->value('name');
                    }
                    if (!$courseName) {
                        throw new ValidateException('课程不存在');
                    }
                    $updateCourseAppointment['courseName'] = $courseName;
                }
                $courseAppointment->save($updateCourseAppointment);
            } catch (Exception|PDOException|ValidateException $e) {
                throw new ValidateException($e->getError());
            } finally {
                foreach ($locks as $lock) {
                    Cache::delete($lock);
                }
            }
            Db::commit();
        } catch (Exception|PDOException|ValidateException $e) {
            Db::rollback();
            $this->setError($e->getMessage());
            return false;
        }
        return true;
    }

    public function cancelAppointment(array $cancel, string $organizationId = null)
    {
        Db::startTrans();
        try {
            $id = $cancel['id'];
            $coreCourseAppointment = new CoreCourseAppointment();
            $courseAppointment = $coreCourseAppointment->where(['id' => $id])->find();
            if (!$courseAppointment) {
                throw new ValidateException('预约记录不存在');
            }
            $studentOrganizationId = $courseAppointment['studentOrganizationId'];
            $organization = new Organization();
            if ($organizationId) {
                $sonOrganization = $organization->getSonOrganization($organizationId);
                if (!in_array($studentOrganizationId, $sonOrganization)) {
                    throw new ValidateException('操作失败，权限不足');
                }
            }
            $appointmentStatus = $courseAppointment['appointmentStatus'];
            if (!in_array($appointmentStatus, [AppointmentStatusEnum::PENDING_CLASS, AppointmentStatusEnum::EXPIRED])) {
                throw new ValidateException('取消预约失败，预约状态不为待上课或已过期');
            }
            //取消预约
            $cnt = $coreCourseAppointment->update([
                'appointmentStatus' => AppointmentStatusEnum::CANCEL,
                'cancelReason' => $cancel['cancelReason'] ?? ''
            ], [
                'id' => $id,
                'appointmentStatus' => $appointmentStatus
            ]);
            if (!$cnt) {
                throw new ValidateException('取消预约失败，请稍后重试');
            }

            $coreTrainerTime = new CoreTrainerTime();
            $coreTrainerTime->removeByIdleAndTrainerAndDateAndTime($courseAppointment['trainerId'], $courseAppointment['date'], $courseAppointment['startTime'], $courseAppointment['endTime']);
            $trainerTimeList = $coreTrainerTime->listNotIdleByTrainerAndDateIn($courseAppointment['trainerId'], [$courseAppointment['date']]);
            $trainerTimeNewList = $coreTrainerTime->getPlanTimeIdle($trainerTimeList);
            if (!empty($trainerTimeNewList)) {
                $insertData = array_filter($trainerTimeNewList, function ($item) {
                    return !isset($item['id']);
                });
                // 过滤出需要更新的数据（id 不为空）
                $updateData = array_filter($trainerTimeNewList, function ($item) {
                    return isset($item['id']);
                });
                if (!empty($insertData)) {
                    $coreTrainerTime->saveAll($insertData);
                }
                if (!empty($updateData)) {
                    foreach ($updateData as $item) {
                        $coreTrainerTime->update($item, ['id' => $item['id']]);
                    }
                }
            }
            $studentId = $courseAppointment['studentId'];
            $student = Student::where(['id' => $studentId])->find();
            $courseFullType = $courseAppointment['courseFullType'];
            $courseFullTypeEnum = new CourseFullTypeEnum();
            $classHourType = $courseFullTypeEnum->getClassHourType($courseFullType);
            if ($classHourType) {
                $accountClassHourService = new AccountClassHourService();
                $cnt = $accountClassHourService->studentReleaseFrozen($student, $classHourType, 1);
                if (!$cnt) {
                    throw new ValidateException($accountClassHourService->getError());
                }
            }
            Db::commit();
        } catch (Exception|PDOException|ValidateException $e) {
            Db::rollback();
            $this->setError($e->getMessage());
            return false;
        }
        return true;
    }

    public function trialClassAppointment($appointment)
    {
        Db::startTrans();
        try {
            $trialClassOrderId = $appointment['trialClassOrderId'];
            $trainerId = $appointment['trainerId'];
            $coreTrialClassOrder = new CoreTrialClassOrder();
            $classOrder = $coreTrialClassOrder->where(['id' => $trialClassOrderId])->find();
            if (!$classOrder) {
                throw new ValidateException('体验课订单不存在');
            }
            $scheduleStatus = $classOrder['scheduleStatus'];
            if (empty($scheduleStatus)) {
                throw new ValidateException('体验课订单不存在');
            }
            if ($scheduleStatus == TcScheduleStatusEnum::E4) {
                throw new ValidateException('该订单已排课');
            }
            if ($classOrder['trainerId'] != null) {
                $trainerId = $classOrder['trainerId'];
            }
            $courseFullType = CourseFullTypeEnum::EXPERIENCE;
            $studentId = $classOrder['studentId'];
            $courseId = $appointment['courseId'];
            $appointmentNum = 1;
            $appointmentInterval = AppointmentIntervalEnum::ONE_0;
            $appointmentDate = $appointment['date'];
            $appointmentStartTime = $appointment['startTime'];
            $courseDeliveryType = $appointment['courseDeliveryType'];
            $timeAppointment = [
                'studentId' => $studentId,
                'courseId' => $courseId,
                'courseType' => CoreCourseTypeEnum::SENTENCE,
                'courseFullType' => $courseFullType,
                'courseDeliveryType' => $courseDeliveryType,
                'appointmentNum' => $appointmentNum,
                'appointmentInterval' => $appointmentInterval,
                'trainerId' => $trainerId,
                'date' => $appointmentDate,
                'startTime' => $appointmentStartTime,
            ];
            $cnt = $this->timeAppointment($timeAppointment);
            if (!$cnt) {
                throw new ValidateException($this->getError());
            }
            $cnt = $coreTrialClassOrder->update([
                'scheduleStatus' => TcScheduleStatusEnum::E4,
                'trainerId' => $trainerId
            ], [
                'id' => $trialClassOrderId,
                'scheduleStatus' => $scheduleStatus
            ]);
            if (!$cnt) {
                throw new ValidateException('排课失败，请刷新后重试');
            }
            Db::commit();
        } catch (Exception|PDOException|ValidateException $e) {
            Db::rollback();
            $this->setError($e->getMessage());
            return false;
        }
        return true;
    }

    public function appointmentUpdateDB($student, $courseFullType, $dateList, $trainerIds, $trainerTimeList, $courseAppointmentList)
    {
        try {
            $trainerTimeNewList = [];
            $trainerTimeListMap = [];
            foreach ($trainerTimeList as $trainerTime) {
                $trainerTimeListMap[$trainerTime['trainerId']][] = $trainerTime;
            }
            $coreTrainerTime = new CoreTrainerTime();
            foreach ($trainerTimeListMap as $key => $values) {
                $dateGroupMap = [];
                foreach ($values as $item) {
                    $dateGroupMap[$item['date']][] = $item;
                }
                foreach ($dateGroupMap as $date => $times) {
                    $list = $coreTrainerTime->getPlanTimeIdle($times);
                    $trainerTimeNewList = array_merge($list, $trainerTimeNewList);
                }
            }

            $insertData = array_filter($trainerTimeNewList, function ($item) {
                return !isset($item['id']);
            });
            // 过滤出需要更新的数据（id 不为空）
            $updateData = array_filter($trainerTimeNewList, function ($item) {
                return isset($item['id']);
            });
            $coreTrainerTime->removeByIdleAndTrainerAndDateIn($trainerIds, $dateList);
            if (!empty($insertData)) {
                $coreTrainerTime->saveAll($insertData);
            }
            if (!empty($updateData)) {
                foreach ($updateData as $item) {
                    $coreTrainerTime->update($item, ['id' => $item['id']]);
                }
            }
            $coreCourseAppointment = new CoreCourseAppointment();
            $coreCourseAppointment->saveAll($courseAppointmentList);
            $courseFullTypeEnum = new CourseFullTypeEnum();
            $classHourType = $courseFullTypeEnum->getClassHourType($courseFullType);
            if ($classHourType) {
                $accountClassHourService = new AccountClassHourService();
                $accountClassHourService->studentFrozen($student, $classHourType, count($courseAppointmentList));
            }
        } catch (Exception|PDOException|ValidateException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return true;
    }


}
